﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace StringDefs.Grammar
{
    using Common;

    public static class Grammar
    {

        /// <summary>
        /// Returns true if the passed argument is a palindrome. Otherwise returns false.
        /// </summary>
        /// <param name="phrase"></param>
        /// <returns></returns>
        public static bool IsPalindrome(this string phrase)
        {
            //It works for all palindromes.
            //Check for these 
            //Madam I'm Adam
            //Was it a cat I saw!
            phrase = phrase.StripWhiteSpaces().Trim().ToLower();

            phrase = phrase.StripChars(new[] { ',', '.', '`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', ':', ';', '"', '\'', '<', '>', '?', '/', '~', '“', '”', '’' });

            if (phrase.CompareTo(phrase.Reverse()) == 0)
            {
                return true;
            }
            
            return false;
        }

        /// <summary>
        /// Returns true if the strings are anagram of each other.
        /// </summary>
        /// <param name="a">The first string</param>
        /// <param name="b">The second string</param>
        /// <returns></returns>
        public static bool IsAnagram(this string a, string b)
        {
            bool status = true;
            a = a.StripWhiteSpaces().ToLower();
            b = b.StripWhiteSpaces().ToLower();
            if (a.Length != b.Length)
                status = false;

            else
            {
                IList<char> ac = a.Alphabets();
                IList<char> bc = b.Alphabets();
                for (int i = 0; i < ac.Count; i++)
                    if (ac[i] != bc[i])
                    {
                        status = false;
                        break;
                    }
            }
            return status;
        }

        /// <summary>
        /// Checkes whether a sentence is written in passive voice or not.
        /// </summary>
        /// <param name="sentence">The argument sentence</param>
        /// <returns>true or false depending on the </returns>

        public static bool IsPassiveVoice(this string sentence)
        {
            List<string> kgrams = (List<string>) sentence.NGrams(2);
            foreach (string k in kgrams)
                if (k.Equals("be") || k.Equals("ne") || k.Equals("by") || k.Equals("ed"))
                    return true;
            return false;
        }

        /// <summary>
        /// Finds the needleman's distance between two strings
        /// </summary>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <param name="GapCost"></param>
        /// <returns></returns>
        public static int NeedlemanDistance(this string first, string second, int GapCost)
        {
            int cost = 0;
            int n = first.Length;
            int m = second.Length;
            if (n == 0)
                return m;
            if (m == 0)
                return n;
            int[][] d = new int[n + 1][];
            for (int i = 0; i <= n; i++)
                d[i] = new int[m + 1];

            for (int i = 0; i < m; i++)
                d[0][i] = 0;
            for (int i = 0; i < n; i++)
                d[i][0] = 0;
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j <= m; j++)
                {
                    if (first[i - 1] == second[j - 1])
                        cost = 0;
                    else
                        cost = 1;
                    d[i][j] = _MinOfThree(d[i - 1][j] + GapCost, d[i][j - 1] + GapCost, d[i - 1][j - 1] + cost);

                }
            }
            return d[n][m];
        }

        private static int _MinOfThree(int a, int b, int c)
        {

            int min = 0;
            if (a <= b && a <= c)
                min = a;
            if (b <= a && b <= c)
                min = b;
            if (c <= a && c <= b)
                min = c;
            return min;
        }

        /// <summary>
        /// Calculates the Hamming Distance between two strings
        /// </summary>
        /// <param name="first">The first string</param>
        /// <param name="second">The second string</param>
        /// <returns></returns>
        public static int HammingDistance(string first, string second)
        {
            int dis = 0;
            if (first.Length == second.Length)
            {
                for (int i = 0; i < first.Length; i++)
                {
                    if (first[i] != second[i])
                        dis++;
                }
                return dis;
            }
            else
            {
                throw new Exception("Hamming distance is only defined for strings of same length.");
            }

        }
        /// <summary>
        /// Checks whether two levenshtein distances are equal or not
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public static bool IsSameLevenshtein(System.String a, string b, string t)
        {
            return LevenshteinDistance(a, t) == LevenshteinDistance(b, t);
        }
        /// <summary>
        /// Calculates the Levenshtein distance between two strings
        /// </summary>
        /// <param name="s">The first string</param>
        /// <param name="t">The second string</param>
        /// <returns></returns>
        public static int LevenshteinDistance(string s, string t)
        {
            int n = s.Length;
            int m = t.Length;
            int[,] d = new int[n + 1, m + 1];

            // Step 1
            if (n == 0)
            {
                return m;
            }

            if (m == 0)
            {
                return n;
            }

            // Step 2
            for (int i = 0; i <= n; d[i, 0] = i++)
            {
            }

            for (int j = 0; j <= m; d[0, j] = j++)
            {
            }

            // Step 3
            for (int i = 1; i <= n; i++)
            {
                //Step 4
                for (int j = 1; j <= m; j++)
                {
                    // Step 5
                    int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;

                    // Step 6
                    d[i, j] = Math.Min(
                        Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
                        d[i - 1, j - 1] + cost);
                }
            }
            // Step 7
            return d[n, m];
        }
        
    }
}
